#pragma once
#include <vector>
#include <algorithm>

class ZNumber {
 public:
  ZNumber();
  ZNumber(const ZNumber& f);  // NOLINT
  ZNumber(int32_t v);         // NOLINT
  ZNumber(uint32_t v);        // NOLINT
  ZNumber(double f);          // NOLINT

  ZNumber& operator+=(const ZNumber& other);
  ZNumber& operator-=(const ZNumber& other);
  ZNumber& operator*=(const ZNumber& other);
  ZNumber& operator/=(const ZNumber& other);
  ZNumber& operator%=(const ZNumber& other);

  ZNumber operator+(double v);
  ZNumber operator-(double v);
  ZNumber operator*(double v);
  ZNumber operator/(double v);
  ZNumber operator%(double v);

  ZNumber& operator+=(double f);
  ZNumber& operator-=(double f);
  ZNumber& operator*=(double f);
  ZNumber& operator/=(double f);
  ZNumber& operator%=(double f);

  ZNumber operator+(int32_t v);
  ZNumber operator-(int32_t v);
  ZNumber operator*(int32_t v);
  ZNumber operator/(int32_t v);
  ZNumber operator%(int32_t v);

  ZNumber& operator+=(int32_t v);
  ZNumber& operator-=(int32_t v);
  ZNumber& operator*=(int32_t v);
  ZNumber& operator/=(int32_t v);
  ZNumber& operator%=(int32_t v);

  ZNumber operator+(const ZNumber& other) const;
  ZNumber operator-(const ZNumber& other) const;
  ZNumber operator-() const;
  ZNumber operator*(const ZNumber& other) const;
  ZNumber operator/(const ZNumber& other) const;
  ZNumber operator%(const ZNumber& other) const;
  ZNumber& operator=(const ZNumber& other);

  bool operator==(const ZNumber& other) const;
  bool operator!=(const ZNumber& other) const;
  bool operator>(const ZNumber& other) const;
  bool operator>=(const ZNumber& other) const;
  bool operator<(const ZNumber& other) const;
  bool operator<=(const ZNumber& other) const;

  bool operator==(int32_t v) const;
  bool operator!=(int32_t v) const;
  bool operator>(int32_t v) const;
  bool operator>=(int32_t v) const;
  bool operator<(int32_t v) const;
  bool operator<=(int32_t v) const;

  bool operator==(uint32_t v) const;
  bool operator!=(uint32_t v) const;
  bool operator>(uint32_t v) const;
  bool operator>=(uint32_t v) const;
  bool operator<(uint32_t v) const;
  bool operator<=(uint32_t v) const;

  bool operator==(double v) const;
  bool operator!=(double v) const;
  bool operator>(double v) const;
  bool operator>=(double v) const;
  bool operator<(double v) const;
  bool operator<=(double v) const;

  bool IsZero() const;
  ZNumber Sqrt() const;

  int64_t GetValue() const;
  void SetValue(int64_t value);

  static int64_t GetAccuracyValue() { return kAccuracyValue; }

  static ZNumber MakeByReal(int64_t value);

  double ToFloat() const;

  int64_t ToInt() const;

  uint32_t ToGrid() const;

  ZNumber Ceil(ZNumber resolution) const;

  ZNumber Floor(ZNumber resolution) const;

  ZNumber Abs() const;

  ZNumber Mod(ZNumber mod) const;

 private:
  static const int64_t kAccuracyValue;

  int64_t value_;
};

extern ZNumber FloatSqrt(const ZNumber& value);